exception Free of string (* en cas de référence à une variable non liée *)

(* --------------------------------------------------- *)

let affiche_tableau tab =
	let taille = Array.length tab in
	for indice = 0 to pred taille do
	print_int tab.(indice);
	print_string " ";
done;
print_newline();
;;

type tableau = {
	mutable taille : int ;
	tab : int array
}
;;

type liste_variable_declaration = 
| Liste_Vide 
| Liste_Declarations of tableau
;;

(* changer en Array *)
let variables = Hashtbl.create 100 ;;

let get_variable_list (nom_variable) =
	if (Hashtbl.mem variables nom_variable) then
  	(* it exists *)
  		Hashtbl.find variables nom_variable
	else
	 	Liste_Vide (* id doesn't exists *)
;;

let get_variable (nom_variable) =
	let liste_variable = get_variable_list nom_variable in
	match liste_variable with 
	| Liste_Vide ->
		print_endline ("erreur: variable ["^nom_variable^"] utilisée, mais non declarée");
		(-1)
	| Liste_Declarations tableau ->
		Array.get tableau.tab tableau.taille
;;

let set_variable (nom_variable,valeur) =
	(* récupérer l'ancienne valeur de variable *)
	let liste_variable = get_variable_list (nom_variable) in
	match liste_variable with
	| Liste_Vide ->
	(* variable n'etait pas lié  *)
	let tabl = Array.make 20 0 in
	begin
		Array.set tabl 0 valeur;
		(*affiche_tableau tabl;*)
		let tablo : tableau = {taille= 0; tab= tabl} in
		let liste_decl = Liste_Declarations(tablo) in 
		Hashtbl.add variables nom_variable liste_decl
	end
	(* variable avait une valeur *)
	| Liste_Declarations tableau ->
	begin 
	tableau.taille <- tableau.taille+1;
	Array.set tableau.tab tableau.taille valeur
end
;;

let unset_variable (variable) =
	let liste_variable = get_variable_list (variable) in
	match liste_variable with
	| Liste_Vide -> 
	print_string ""
	| Liste_Declarations tableau -> 
	tableau.taille <- tableau.taille-1
;;

let unset_variables(variables) = 
	List.iter (fun (v) -> 
		begin
			unset_variable(v);
			flush stdout
		end
    ) variables 
;;

(* --------------------------------------------------- *)

module H = Hashtbl.Make(struct
	type t = string
	let equal = (=)
	let hash = Hashtbl.hash
end);;


type propriete_fonction =
| Vide
| Procedure of int
| Fonction of int * string
;;

let fonctions = H.create 100;;

let fonctions_predefinies = ["print"];;

let hashtbl_keys h = H.fold (fun key _ l -> key :: l) h []
;;

let print_keys h = 
	begin
		print_endline "";
		print_string "keys=[";
		List.iter (fun key -> print_string (key^",") ) (hashtbl_keys h);
		print_endline "]";
		print_endline ""
	end

let tester_fonction (nom_fonction,proprietes) =
	if (List.mem nom_fonction fonctions_predefinies) then
		true
	else
		if (H.mem fonctions nom_fonction) then
		begin
			let props = H.find fonctions nom_fonction in
			match props with
			| Vide ->
			begin
				match proprietes with
				| Vide -> true	
				| _ -> false
			end
			| Procedure nb_args->
			begin
				match proprietes with
				| Procedure mes_nb_args ->
				nb_args == mes_nb_args
				| Fonction (mes_nb_args,montype) ->
				nb_args == mes_nb_args
				| Vide -> 
				false
			end
			| Fonction (nb_args,letype) ->
			begin
				match proprietes with
				| Procedure mes_nb_args ->
				(nb_args == mes_nb_args) 
				| Fonction (mes_nb_args,montype) ->
				(nb_args == mes_nb_args) 
				| Vide -> 
				false
			end
		end
		else
			false
;;

(* propriete = (nb_arguments,type_retour) *)
let ajouter_fonction (nom_fonction,proprietes) =
	if H.mem fonctions nom_fonction then
		false
	else
	begin
		H.add fonctions nom_fonction proprietes;
		true
	end
;;